package com.example.shiro.common.config.shiro;

import com.alibaba.fastjson.JSONObject;
import com.example.shiro.common.response.CommonCode;
import com.example.shiro.common.util.JWTToken;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.PrintWriter;
import java.util.Objects;

/**
 * @auther 严旭平
 * @date 2020/4/2415:55
 * @Description JWT权限过滤器
 */
public class JWTFilter extends BasicHttpAuthenticationFilter {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

//    /**
//     * 判断用户是否想要登入。
//     * 检测header里面是否包含Authorization字段即可
//     */
    @Override
    protected boolean isLoginAttempt(ServletRequest request, ServletResponse response) {
        HttpServletRequest req = (HttpServletRequest) request;
        String authorization = req.getHeader("token");
        if(  Objects.isNull( authorization ) ){
            logger.info("请求头没有token字段，返回token验证异常");
            return false;
        }
        return true;
    }

    /**
     *获取token,执行认证授权操作
     */
    @Override
    protected boolean executeLogin(ServletRequest request, ServletResponse response)  {

        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        String authorization = httpServletRequest.getHeader("token");

        JWTToken token = new JWTToken( authorization );
        // 提交给realm进行登入，如果错误他会抛出异常并被捕获
        try{
            /**
             * 进入自定义realm执行身份验证操作
             */
            getSubject(request, response ).login( token );
        }catch (AuthenticationException e){
            e.printStackTrace();
//            throw new AuthenticationException( e.getMessage()  );
            logger.info("接收到用户认证，权限拦截异常，异常信息为" + e.getMessage());
            responseError( response ,e.getMessage()  );
            return false;
        }
        // 如果没有抛出异常则代表登入成功，返回true
        return true;
    }

    /**
     * 这里我们详细说明下为什么最终返回的都是true，即允许访问
     * 例如我们提供一个地址 GET /article
     * 登入用户和游客看到的内容是不同的
     * 如果在这里返回了false，请求会被直接拦截，用户看不到任何东西
     * 所以我们在这里返回true，Controller中可以通过 subject.isAuthenticated() 来判断用户是否登入
     * 如果有些资源只有登入用户才能访问，我们只需要在方法上面加上 @RequiresAuthentication 注解即可
     * 但是这样做有一个缺点，就是不能够对GET,POST等请求进行分别过滤鉴权(因为我们重写了官方的方法)，但实际上对应用影响不大
     */
    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
       logger.debug("进入到权限验证方法isAccessAllowed中开始验证");
        /**
         * 如果请求方法中带有免权限注解，则直接放行.否则执行token验证和登录验证(待添加)
         * @@@@ 可以动态分配权限认证，通过数据库查询地址权限
         */

       logger.debug("登录检查token");
       if( !isLoginAttempt(request, response)  ){
           return false;
       }

        return  executeLogin(request, response);

    }

    /**
     * 抛出异常返回信息
     */
    private void responseError( ServletResponse response ,String ex ){
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json; charset=utf-8");
        PrintWriter out = null;
        try {
            JSONObject res = new JSONObject();
            res.put("code", CommonCode.TOKEN_ERROR.getCode() );
            res.put("msg",  CommonCode.TOKEN_ERROR.getMsg() + "," + ex  );
            res.put("success", false);
            res.put("data","" );
            out = response.getWriter();
            out.append( res.toString() );
        } catch (Exception e) {
            logger.debug("json转换异常");
        } finally {
            out.close();
        }
    }

}
